﻿using System.Collections.Generic;
using System.Windows.Forms;
using Ch.Admin.Bfs.Sms.Sdmx.Extension.CodeBook.Meta;
using SdmxMl.Manager;
using SdmxMl.Structure;
using SdmxMl.Common;
using System;

namespace bfs.sms.sdmx.util.Variable
{
    /// <summary>
    /// Class for collecting and preparing information for Excel sheets creation
    /// </summary>
    public class SheetConstructor
    {
        #region Internal members

        private SmManager sm;
        private RefVarHelper refHelper;
        private VarCatalogOptions options;

        #endregion

        #region Properties

        /// <summary> List holding info for creation of Excel sheets</summary>
        public List<VariableSheet> SheetList { get; private set; }

        /// <summary> Optional codelist representing summary of all quality codes used in catalog</summary>
        public CodeListType QualityCodelist { get; private set; }

        /// <summary> List of non finalized artefacts used in catalog</summary>
        public IList<Artefact> UnfinalizedArtefactList { get; private set; }

        #endregion

        #region C'tor

        /// <summary> Creates container for Excel sheets to create </summary>
        public SheetConstructor()
        {
            SheetList = new List<VariableSheet>();
        }
        #endregion

        #region Inventory of non finalized artefact used

        private List<Artefact> GetUnfinalizedArtefacts()
        {
            List<Artefact> listUnfinalized = new List<Artefact>();
            if (SheetList != null && SheetList.Count > 0)
            {
                foreach (VariableSheet sheet in SheetList)
                {
                    if (sheet.Scheme.IsFinal == false && listUnfinalized.Contains(sheet.Scheme) == false)
                        listUnfinalized.Add(sheet.Scheme);
                    foreach (Variable var in sheet.VarList)
                    {
                        if (var.Codelist != null && var.Codelist.IsFinal == false && listUnfinalized.Contains(var.Codelist) == false)
                            listUnfinalized.Add(var.Codelist);
                        if (var.Qualitylist != null && var.Qualitylist.IsFinal == false && listUnfinalized.Contains(var.Qualitylist) == false)
                            listUnfinalized.Add(var.Qualitylist);
                    }
                }
           }

            return listUnfinalized;
        }

        private void AddUnfinalizedDsd(IList<Artefact> listUnfinalized, TreeNode nodeModuleDsd)
        {
            foreach (TreeNode nodeDsd in nodeModuleDsd.Nodes)
            {
                if (nodeDsd.Checked)
                {
                    // Get DSD
                    KeyFamilyType dsd = nodeDsd.Tag as KeyFamilyType;
                    if (dsd.IsFinal == false)
                        listUnfinalized.Add(dsd);
                }
            }
        }

        #endregion

        #region Public Method for inventory list
        /// <summary>
        /// Construct list of items to export according to tree selection
        /// </summary>
        /// <param name="manager">meta cache & loader</param>
        /// <param name="nodesModule">node holding children modules</param>
        /// <param name="mode">mode for managing quality info</param>
        public void ConstructList(SmManager manager, TreeNodeCollection nodesModule, VarCatalogOptions catalogOptions)
        {
            SheetList.Clear();
            sm = manager;
            refHelper = new RefVarHelper(sm);
            options = catalogOptions;

            // A conceptScheme is required
            if (nodesModule[0].Nodes.Count == 0)
                throw new ApplicationException("A catalog (ConceptScheme) is required!");

            // Scan module of catalogs
            foreach (TreeNode nodeScheme in nodesModule[0].Nodes)
            {
                // Whole catalog ?
                if (nodeScheme.Checked)
                {
                    VariableSheet sheet = new VariableSheet(nodeScheme.Tag as ConceptSchemeType);
                    SheetList.Add(sheet);
                    FillSheet(sheet);
                }
                else // selected domains in catalog
                {
                    foreach (TreeNode nodeConcept in nodeScheme.Nodes)
                    {
                        if (nodeConcept.Checked)
                        {
                            VariableSheet sheet = new VariableSheet(
                                nodeScheme.Tag as ConceptSchemeType,
                                nodeConcept.Tag as ConceptType);
                            SheetList.Add(sheet);
                            FillSheet(sheet);
                        }
                    }
               }
            }

            // Override Codelist & textformat from DSD
            ApplyOverrideFromDsd(nodesModule[1]);

            //Create for each codelist used a Excel sheet valid id
            CreateCodeSheetId();

            if (options.Quality == QualityMode.AttributeAssignation)
                ApplyQualityFromDsd(nodesModule[1]);
            else if (options.Quality == QualityMode.SubConceptFlag)
                ApplyQualityFromSubConcept(SheetList);

            // Non finalized material ?
            UnfinalizedArtefactList = GetUnfinalizedArtefacts();
            AddUnfinalizedDsd(UnfinalizedArtefactList, nodesModule[1]);

            // Do we have a Quality codelist that resumes all quality codes
            if (nodesModule[2].Nodes.Count > 0)
            {
                QualityCodelist = nodesModule[2].Nodes[0].Tag as CodeListType;
                if (QualityCodelist.IsFinal == false && UnfinalizedArtefactList.Contains(QualityCodelist) == false)
                    UnfinalizedArtefactList.Add(QualityCodelist);
            }
        }
        #endregion

        #region Override concept info with the one in selected DSD's

        /// <summary>
        /// Scan DSD to override concept information of variables catalog
        /// </summary>
        /// <param name="nodeModuleDsd">Root node of all DSD</param>
        protected void ApplyOverrideFromDsd(TreeNode nodeModuleDsd)
        {
            foreach (TreeNode nodeDsd in nodeModuleDsd.Nodes)
            {
                if (nodeDsd.Checked)
                {
                    // Get DSD
                    KeyFamilyType dsd = nodeDsd.Tag as KeyFamilyType;

                    //Scan attributes 
                    List<BaseDimensionType> lbd = refHelper.PrepareVarList(dsd, false);
                    foreach (BaseDimensionType bd in lbd)
                    {
                        AttributeType att = bd as AttributeType;

                        // if attachement, ignore attribute dimension
                        if (!(att != null && att.AttachmentMeasureList.Count > 0 && att.AttachmentLevel == AttributeType.AttachmentLevelType.Observation))
                        {
//                            if (bd.ConceptInfo.Id == "TO_PERSON_DATEOFBIRTH")
                            // Something to override ?
                            if (bd.TextFormat.TextType != TextTypeType.Undefined || bd.CodelistInfo.IsFilled)
                                OverrideConcept(bd);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Apply on convept of varsheet the overriden codelist or textformat (if any) held in DSD basedimension
        /// </summary>
        /// <param name="bd">the dsd dimension holding overriden infos</param>
        private void OverrideConcept(BaseDimensionType bd)
        {
            foreach (VariableSheet sheet in SheetList)
            {
                if (sheet.Scheme.FullIdent == bd.ConceptSchemeInfo.FullIdent)
                {
                    Variable var = sheet.VarList.Find(x => x.Concept.Id == bd.ConceptInfo.Id);
                    if (var != null)
                    {
                        if (bd.TextFormat.TextType != TextTypeType.Undefined)
                            var.FormatType = bd.TextFormat;
                        if (bd.CodelistInfo.IsFilled)
                            var.Codelist = refHelper.GetCodelist(bd.CodelistInfo);
                        break;
                    }
                }
            }
        }

        #endregion

        #region Quality codes retrieving

        protected void ApplyQualityFromDsd(TreeNode nodeModuleDsd)
        {
            foreach (TreeNode nodeDsd in nodeModuleDsd.Nodes)
            {
                if (nodeDsd.Checked)
                {
                    // Get DSD
                    KeyFamilyType dsd = nodeDsd.Tag as KeyFamilyType;

                    //Scan attributes 
                    List<BaseDimensionType> lbd = refHelper.PrepareVarList(dsd, false);
                    foreach (BaseDimensionType bd in lbd)
                    {
                        AttributeType att = bd as AttributeType;

                        // if attachement, get list
                        if (att != null && att.AttachmentMeasureList.Count > 0 && att.AttachmentLevel == AttributeType.AttachmentLevelType.Observation)
                        {
                            // foreach in list locate corresponding concept in Sheetlist
                            foreach (string varId in att.AttachmentMeasureList)
                            {
                                BaseDimensionType bdTarget = dsd.LocateItem(varId);
                                // if found, add quality code
                                if (bdTarget != null)
                                {
                                    AddQualityCodes(att, bdTarget);
                                }
                            }
                        }
                    }

                }
            }

        }
        protected void ApplyQualityFromSubConcept(List<VariableSheet> sheetList)
        {
            foreach (VariableSheet vs in sheetList)
            {
                foreach (Variable var in vs.VarList)
                {
                    // Get eventual flag
                    string conceptflagId = var.Concept.Id + "_F";
                    ConceptType c = vs.Scheme.ConceptList.LocateItem(conceptflagId) as ConceptType;
                    if (c != null && c.Parent == var.Concept.Id && c.CoreRepresentation.IsFilled)
                        var.Qualitylist = refHelper.GetCodelist(c.CoreRepresentation);
                }
            }
        }

        private void CreateCodeSheetId()
        {
            int sheetnameIndex = 0;
            Dictionary<string, int> dicoCodeWorkSheetId = new Dictionary<string, int>(100);

            foreach (VariableSheet sheet in SheetList)
            {
                foreach (Variable var in sheet.VarList)
                {
                    if (var.Codelist != null)
                    {
                        if (dicoCodeWorkSheetId.ContainsKey(var.Codelist.FullIdent) == false)
                        {
                            var.CodeSheetId = (++sheetnameIndex).ToString();
                            dicoCodeWorkSheetId.Add(var.Codelist.FullIdent, sheetnameIndex);
                        }
                        else
                        {
                            var.CodeSheetId = dicoCodeWorkSheetId[var.Codelist.FullIdent].ToString();
                        }
                    }
                }
            }
        }

        private void AddQualityCodes(AttributeType att, BaseDimensionType bd)
        {

            foreach (VariableSheet sheet in SheetList)
            {
                if (sheet.Scheme.FullIdent == bd.ConceptSchemeInfo.FullIdent)
                {
                    Variable var = sheet.VarList.Find(x => x.Concept.Id == bd.ConceptInfo.Id);
                    if (var != null)
                    {
                        CodeListType cltQuality = refHelper.GetCodelist(att.CodelistInfo);
                        var.Qualitylist = cltQuality;
                        break;
                    }
                }
            }
        }
        #endregion

        #region Helpers for parsing concept informations

        private void FillSheet(VariableSheet sheet)
        {
            ConceptNode root = ProduceHierarchy(sheet);
            if (sheet.IsFullScheme == false)
                root = root.LocateItemFromId(sheet.RootConcept.Id, root.ChildNodes);

            RecurseLeaf(root, sheet);
            
        }

        private void RecurseLeaf(ConceptNode node, VariableSheet sheet)
        {
            if (node.ChildNodes.Count > 0)
            {
                foreach (ConceptNode ndChild in node.ChildNodes)
                    RecurseLeaf(ndChild, sheet);
            }
            else
            {
                // We are in leaf
                Variable v = new Variable(node.Concept, refHelper.GetCodelist(node.Concept.CoreRepresentation));
                sheet.VarList.Add(v);
            }
        }

        private ConceptNode ProduceHierarchy(VariableSheet sheet)
        {

            ConceptNode root = new ConceptNode(sheet.RootConcept);
            List<ISdmxHierarchyListItem> listItems = new List<ISdmxHierarchyListItem>(sheet.Scheme.ConceptList);
            FillRecurse(root.ChildNodes, listItems, "");

            return root;
            
        }

        private void FillRecurse(List<ConceptNode> nodes, List<ISdmxHierarchyListItem> listItems, string parentId)
        {
            List<ISdmxHierarchyListItem> listChild = GetChildList(parentId, listItems);
            foreach (ISdmxHierarchyListItem item in listChild)
            {
                ConceptNode childNode = new ConceptNode(item as ConceptType);
                if (options.Quality == QualityMode.SubConceptFlag &&
                    item.Id.EndsWith("_F") && item.Id.Substring(0, item.Id.Length - 2) == parentId)
                    ;
                else
                    nodes.Add(childNode);
                listItems.Remove(item);
                FillRecurse(childNode.ChildNodes, listItems, item.Id);
            }
        }
        
        protected List<ISdmxHierarchyListItem> GetChildList(string parentId, List<ISdmxHierarchyListItem> listItems)
        {
            List<ISdmxHierarchyListItem> listChild = new List<ISdmxHierarchyListItem>();

            foreach (ISdmxHierarchyListItem item in listItems)
            {
                if (item.Parent == parentId)
                    listChild.Add(item);
            }

            return listChild;
        }

    }
        #endregion

    //---------------------------------------------------------

    #region Helper class for a hierarchy representation of concepts

    internal class ConceptNode
    {
        public ConceptType Concept { get; set; }
        public List<ConceptNode> ChildNodes;

        internal ConceptNode(ConceptType c)
        {
            Concept = c;
            ChildNodes = new List<ConceptNode>();
        }

        internal void Add(ConceptType c)
        {
            ChildNodes.Add(new ConceptNode(c));
        }

        public ConceptNode LocateItemFromId(string id,List<ConceptNode> list)
        {
            ConceptNode ret = null;

            foreach (ConceptNode cat in list)
            {
                if (cat.Concept.Id == id)
                {
                    ret = cat;
                    break;
                }
                else
                {
                    ConceptNode rep = LocateItemFromId(id, cat.ChildNodes);
                    if (rep != null)
                        ret = rep;
                }
            }
            return ret;
        }
    }
    #endregion
}
